require 'depac-common/softlayer'
require 'depac-deployment/environment_configuration'
require 'depac-deployment/helper'
require 'depac-common/constants'
require 'depac-common/utils'
require 'pp'
require 'yaml'


# This method provisions a new instance based on the env config(and existing env config - in the case of add instance) provided.
# Parameters:
#  newEnvConfigObj - config object representing the environment
#  buildLabel - the build label to be used for the deployment
#  envName - the name of the environment
#  existingEnvConfigObj - config object representing the exsiting environment(in the case of add instnace)
# 	hn_counter- used only for updateEnv. 
def provisionInstance(newEnvConfigObj, buildLabel, envName, hn_counter, existingEnvConfigObj)

    newEnvConfig = newEnvConfigObj.getEnvironmentConfiguration()
    
	# build and validate the image names
	if newEnvConfig["ifEnableDocker"] == false
	   buildAndValidateImageNames(newEnvConfigObj, buildLabel)
	else
	   buildAndValidateImageNamesForDocker(newEnvConfigObj, buildLabel)
	end 	
	
	keyId = nil
	
	Depac::Utils.ensureKeys(Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH, "cloud-deployment-workspace-key")
    keyId = $slmgr.registerSSHkey(Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH+".pub")
	
	typeNodeInfoMap = Hash.new
	typeArr = getEnvironmentTypes()
	
	Depac::Log.info(Depac::CloudDeploymentConstants::BORDER_LINE)
	
	startProvisionTime = Time.new
	
	##--- Placing Provisioning request ---##
	provisionCounter = 0
	typeArr.each do |type|
		if(newEnvConfig[type] != nil)
			nodeInfoMap = Hash.new
			
#			puts Depac::CloudDeploymentConstants::BORDER_LINE
#			puts "Provisiong #{type.capitalize} instances"
#			puts Depac::CloudDeploymentConstants::BORDER_LINE
			newEnvConfig[type].each do |config|
				role = config[Depac::CloudDeploymentConstants::ROLE]
				vmconfig = config['vmconfig']
				if(vmconfig == nil)
					Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
					Depac::Log.write("ERROR", "vmconfig for the role - #{role} is missing")
					exit
				end
				if (role == Depac::CloudDeploymentConstants::ROLE_DATABASE || role == Depac::CloudDeploymentConstants::ROLE_JMS)
					config[Depac::CloudDeploymentConstants::NO_OF_NODES]=1
				end
				Depac::Log.write("INFO", "Provisioning #{type}:#{role}")
				
				# get the flex image name
				snapshotName = config[Depac::CloudDeploymentConstants::SNAPSHOT]
				
				# retriving the imageId of flex image snapshot
				snapshotDetails = $slmgr.findFlexImageByName(snapshotName)
				snapshotImageId = snapshotDetails['globalIdentifier']
				
				nodeCount = 0
				if(hn_counter!=0)
					nodeCount=hn_counter-1
				elsif(existingEnvConfigObj != nil)
					nodeCount = getNodeCountFromExistingEnv(existingEnvConfigObj.getNodeConfigurationByRoleAndType(role, type))
				end
				
				svrs = Array.new
				counter = 0;
				
				# get the hostname
				if (role == Depac::CloudDeploymentConstants::ROLE_AGENT)
          svrs = getSvrs(config[Depac::CloudDeploymentConstants::AGENT_NODES])
          unless svrs then next; end
					hostnameArr = getHostnames(buildLabel, envName, type[0..2], role, svrs.length, nodeCount)
				elsif (role == Depac::CloudDeploymentConstants::ROLE_INTG)
				  svrs = getSvrs(config[Depac::CloudDeploymentConstants::INTG_NODES])
				  unless svrs then next; end
          hostnameArr = getHostnames(buildLabel, envName, type[0..2], role, svrs.length, nodeCount)
        else
					hostnameArr = getHostnames(buildLabel, envName, type[0..2], role, config[Depac::CloudDeploymentConstants::NO_OF_NODES], nodeCount)	
				end
				
				Depac::Log.write("INFO", "#{role} hostname(s):  #{hostnameArr}")
				Depac::Log.write("INFO", Depac::CloudDeploymentConstants::EMPTY_STRING)
				
				hostnameIPArr = Array.new
			
				hostnameArr.each do |hostname|
		            
		            vmspecconfig=config['vmspecconfig']
		            
					#provisioning the vm 
				    Depac::Log.info("Provisioning server from snapshot #{snapshotImageId} with hostname #{hostname}")
					purchaseOrderDetails = $slmgr.provisionServer(hostname, snapshotImageId, vmconfig, vmspecconfig, keyId,3)

					vmid=$slmgr.getVirtualGuestIdFromOrderDetails(purchaseOrderDetails, hostname)
					#orderId = purchaseOrderDetails['orderId']
					
					#waiting for provision to complete
					#vg=waitForVMTransactionsToComplete(vmid)
					#privateIpAddress = vg['primaryBackendIpAddress']
					
					#Writting the ip in hash map	
					hostnameIPMap = Hash.new
					hostnameIPMap['hostname'] = hostname
					hostnameIPMap['vmid'] = vmid
          hostnameIPMap['status'] = "InProgress"
					#hostnameIPMap['IP'] = privateIpAddress
					
					provisionCounter += 1
					
					if (role == "agent" || role == "intg")
						hostnameIPMap['info'] = svrs[counter]
						counter+=1
					end
					
					hostnameIPArr.push(hostnameIPMap)
					Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
				end
				#puts "#{role} info: #{hostnameIPArr}"
				
				nodeInfoMap[role] = hostnameIPArr
			end
			typeNodeInfoMap[type] = nodeInfoMap
		end
	end
	
	##--- Waiting for the provisioning to complete ---##
	waitCounter = 0
	while waitCounter < provisionCounter do
		
		# The start wait time
		startWaitTime = Time.new
		
		typeArr.each do |type|
			if(typeNodeInfoMap[type] != nil)
				typeNodeInfoMap[type].each do |role, nodeInfoArr|
				
					nodeInfoArr.each do |hostnameIPMap|
						# retrieve vmid from the map
					  status = hostnameIPMap['status']
						vmid = hostnameIPMap['vmid']
						
						if(status != nil)
							hostname = hostnameIPMap['hostname']
							
							# Get virtual guest object from vmid
							vg = $slmgr.getVirtualGuestObject(vmid)
						  
							# check whether the transaction has completed
							isComplete = $slmgr.isTransactionComplete(vg)
						  
							if(isComplete)
								privateIpAddress = vg.getObject()['primaryBackendIpAddress']
								hostnameIPMap['IP'] = privateIpAddress
								
								# remove status entry from the map
                hostnameIPMap.delete('status')
								  
								# remove the vmid entry from the map
								#hostnameIPMap.delete('vmid')
								  
								Depac::Log.write("INFO", "#{type}:#{role}:#{hostname} provisioned...")
								waitCounter += 1
							end
						end
					end
				end
			end
		end
		Depac::Log.write("INFO", Depac::CloudDeploymentConstants::EMPTY_STRING)
		
		# The end wait time
		endWaitTime = Time.new
		
		# The time taken to wait for all the nodes
		elapsedTime = endWaitTime - startWaitTime
		
		# wait for some period of time, before checking for the transactions again
		$slmgr.waitForSomeTime(elapsedTime.to_i)
	end
	endProvisionTime = Time.new
	Depac::Log.write("INFO", "Time taken to provision instances: #{computeDuration(endProvisionTime - startProvisionTime)}")
	
	Depac::Log.write("INFO", Depac::CloudDeploymentConstants::EMPTY_STRING)
	Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
	
	return typeNodeInfoMap
end

# Returns the largest node number assigned to the nodes contained in 'nodeConfig'
def getNodeCountFromExistingEnv(nodeConfig)
  node_count = 0
  
  if (nodeConfig != nil)
    role = nodeConfig[Depac::CloudDeploymentConstants::ROLE]
    node_info = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
    
    if(node_info != nil)
      
      node_info.each do |node_info_each|
        hostname = node_info_each[Depac::CloudDeploymentConstants::HOSTNAME]
        tmp_node_count = hostname.partition("#{role}-")[2].partition('-')[0].to_i
        
        if(tmp_node_count > node_count)
          node_count = tmp_node_count
        end
      end
      #node_count = node_info.size
    end
  end
  return node_count
end

